package laxproxy

import (
	"github.com/panjf2000/gnet"
	"net"
	"sync"
)

// @Desc: 用户态的自定义代理存储内容
type (
	IProxyUserContext interface {
		// 获取要连接的服务器地址 ip:port, 返回Wait可以继续等待
		GetConnectServerAddress() (string, error)
		/// 获取角色id
		GetRoleUid() uint64
		/// 获取角色名字
		GetRoleName() string
	}

	// 与客户端的目标连接上下文
	ProxyBaseContext struct {
		// 目标连接
		serverConn net.Conn
		// 客户端连接
		clientConn gnet.Conn
		// 有一方连接关闭后,发信号,然后关闭另一方
		stopChan chan bool
		// 用户自定义数据
		userCtx IProxyUserContext
		/// 收到客户端的socket消息
		client2serverSocketBuff []byte
		/// 收到服务器的socket消息
		server2clientSocketBuff []byte
		/// 从web发到服务器的消息
		web2ServerBuff []byte
	}

	ProxyContextMgr struct {
		ctxMap map[uint32]*ProxyBaseContext
		uuid   uint32
	}
)

var proxyCtxMgrInstance *ProxyContextMgr
var proxyCtxOnce sync.Once
var webSendMsgMutex sync.Mutex

func getProxyCtxMgr() *ProxyContextMgr {
	proxyCtxOnce.Do(func() {
		proxyCtxMgrInstance = &ProxyContextMgr{
			ctxMap: map[uint32]*ProxyBaseContext{},
			uuid:   0,
		}
	})
	return proxyCtxMgrInstance
}

func (pcm *ProxyContextMgr) RequireContext(ctx *ProxyBaseContext) uint32 {
	pcm.uuid++
	pcm.ctxMap[pcm.uuid] = ctx
	return pcm.uuid
}
func (pcm *ProxyContextMgr) GetContext(uuid uint32) *ProxyBaseContext {
	if ctx, ok := pcm.ctxMap[uuid]; ok {
		return ctx
	}
	return nil
}
func (pcm *ProxyContextMgr) Stop(uuid uint32) {
	if ctx, ok := pcm.ctxMap[uuid]; ok {
		ctx.stopChan <- true
		delete(pcm.ctxMap, uuid)
	}
}
func (pcm *ProxyContextMgr) Write2ServerByRoleId(roleId uint64, msg []byte) bool {
	for _, ctx := range pcm.ctxMap {
		if ctx.userCtx.GetRoleUid() == roleId {
			webSendMsgMutex.Lock()
			if len(ctx.client2serverSocketBuff) == 0 {
				ctx.serverConn.Write(msg)
			} else {
				ctx.web2ServerBuff = append(ctx.web2ServerBuff, msg...)
			}
			webSendMsgMutex.Unlock()
			return true
		}
	}
	return false
}

// / 在线角色列表
func GetOnlineRoleList() []RoleBasicInfo {
	var roles []RoleBasicInfo
	for _, v := range getProxyCtxMgr().ctxMap {
		if v.userCtx.GetRoleUid() > 0 {
			roles = append(roles, RoleBasicInfo{
				RoleId:   v.userCtx.GetRoleUid(),
				RoleName: v.userCtx.GetRoleName(),
			})
		}
	}
	return roles
}
